"""
Pydantic schemas for call tree node data validation and serialization.
"""

from datetime import datetime
from typing import Dict, Any, List, Optional
from pydantic import BaseModel, Field, validator


class NodeSchema(BaseModel):
    """Schema for call tree node data."""
    
    id: Optional[str] = None
    call_tree_id: Optional[str] = None
    
    # Node identification
    name: str = Field(description="Function/method/class name")
    file_path: str = Field(description="Path to source file")
    line_number: int = Field(ge=1, description="Line number in source file")
    language: str = Field(description="Programming language")
    node_type: str = Field(default="function", description="Type of node")
    
    # Function/method details
    signature: Optional[str] = Field(None, description="Function signature")
    documentation: Optional[str] = Field(None, description="Function documentation")
    
    # Call relationships
    caller_node_ids: List[str] = Field(default_factory=list, description="IDs of nodes that call this node")
    callee_node_ids: List[str] = Field(default_factory=list, description="IDs of nodes called by this node")
    
    # Analysis metadata
    call_frequency: int = Field(default=0, ge=0, description="How often this node is called")
    call_context: str = Field(default="", description="Context in which this node is called")
    short_description: str = Field(default="", description="Brief description of the node")
    llm_explanation: str = Field(default="", description="LLM-generated explanation")
    semantic_tags: List[str] = Field(default_factory=list, description="Semantic tags for the node")
    complexity_score: float = Field(default=0.0, ge=0.0, le=100.0, description="Complexity score")
    
    # Timestamps
    created_at: Optional[datetime] = None
    updated_at: Optional[datetime] = None
    
    # Additional metadata
    metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
    
    class Config:
        """Pydantic configuration."""
        json_encoders = {
            datetime: lambda v: v.isoformat() if v else None
        }
        validate_assignment = True
        
    @validator('node_type')
    def validate_node_type(cls, v):
        """Validate node type."""
        allowed_types = ['function', 'method', 'class', 'section', 'module', 'namespace', 'constructor', 'destructor']
        if v not in allowed_types:
            raise ValueError(f'node_type must be one of: {", ".join(allowed_types)}')
        return v
    
    @validator('language')
    def validate_language(cls, v):
        """Validate language."""
        allowed_languages = ['python', 'cpp', 'c', 'fortran', 'typescript', 'javascript', 'latex', 'markdown']
        if v not in allowed_languages:
            raise ValueError(f'language must be one of: {", ".join(allowed_languages)}')
        return v
    
    @validator('semantic_tags')
    def validate_semantic_tags(cls, v):
        """Validate semantic tags."""
        if len(v) > 50:  # Reasonable limit
            raise ValueError('Too many semantic tags (max 50)')
        return v


class NodeCreateSchema(BaseModel):
    """Schema for creating new nodes."""
    
    call_tree_id: str = Field(description="ID of the call tree this node belongs to")
    name: str = Field(description="Function/method/class name")
    file_path: str = Field(description="Path to source file")
    line_number: int = Field(ge=1, description="Line number in source file")
    language: str = Field(description="Programming language")
    node_type: str = Field(default="function", description="Type of node")
    signature: Optional[str] = Field(None, description="Function signature")
    documentation: Optional[str] = Field(None, description="Function documentation")
    metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
    
    @validator('node_type')
    def validate_node_type(cls, v):
        """Validate node type."""
        allowed_types = ['function', 'method', 'class', 'section', 'module', 'namespace', 'constructor', 'destructor']
        if v not in allowed_types:
            raise ValueError(f'node_type must be one of: {", ".join(allowed_types)}')
        return v


class NodeUpdateSchema(BaseModel):
    """Schema for updating nodes."""
    
    name: Optional[str] = None
    file_path: Optional[str] = None
    line_number: Optional[int] = Field(None, ge=1)
    language: Optional[str] = None
    node_type: Optional[str] = None
    signature: Optional[str] = None
    documentation: Optional[str] = None
    caller_node_ids: Optional[List[str]] = None
    callee_node_ids: Optional[List[str]] = None
    call_frequency: Optional[int] = Field(None, ge=0)
    call_context: Optional[str] = None
    short_description: Optional[str] = None
    llm_explanation: Optional[str] = None
    semantic_tags: Optional[List[str]] = None
    complexity_score: Optional[float] = Field(None, ge=0.0, le=100.0)
    metadata: Optional[Dict[str, Any]] = None


class NodeSummarySchema(BaseModel):
    """Schema for node summary data."""
    
    id: str
    name: str
    file_path: str
    line_number: int
    language: str
    node_type: str
    call_frequency: int
    complexity_score: float
    created_at: datetime
    updated_at: datetime
    
    class Config:
        """Pydantic configuration."""
        json_encoders = {
            datetime: lambda v: v.isoformat()
        }


class NodeRelationshipSchema(BaseModel):
    """Schema for node relationships."""
    
    caller_id: str = Field(description="ID of the calling node")
    callee_id: str = Field(description="ID of the called node")
    relationship_type: str = Field(default="calls", description="Type of relationship")
    call_frequency: int = Field(default=1, ge=1, description="Number of times this call occurs")
    call_context: str = Field(default="", description="Context of the call")
    
    @validator('relationship_type')
    def validate_relationship_type(cls, v):
        """Validate relationship type."""
        allowed_types = ['calls', 'inherits', 'imports', 'references', 'instantiates']
        if v not in allowed_types:
            raise ValueError(f'relationship_type must be one of: {", ".join(allowed_types)}')
        return v


class NodeWithRelationsSchema(NodeSchema):
    """Schema for node with relationship information."""
    
    callers: List[NodeSummarySchema] = Field(default_factory=list, description="Nodes that call this node")
    callees: List[NodeSummarySchema] = Field(default_factory=list, description="Nodes called by this node")
    caller_count: int = Field(default=0, description="Number of caller nodes")
    callee_count: int = Field(default=0, description="Number of callee nodes")